package com.elephantgreen.util.zip;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import android.util.Log;

/**
 * Utility class to create and extract zip files.
 * 
 * @author Bishan
 * 
 */
public class ZipFileUtil {

	/**
	 * Given a source location with multiple files this methods will create the
	 * given zip file in the specified location.
	 * 
	 * @param sourcePath
	 *            source directory containing all the files to be zipped.
	 * @param targetPath
	 *            target zip file to be created with path and zip file name;
	 * @throws IOException
	 *             if the path's are not correct
	 */
	public void createZip(File sourcePath, File targetPath) throws IOException {

		File f = new File("/mnt/sdcard/extract/");

		if (f.exists() == false) {
			f.mkdir();
		}

		// create object of FileOutputStream
		FileOutputStream fout = new FileOutputStream(targetPath);

		// create object of ZipOutputStream from FileOutputStream
		ZipOutputStream zout = new ZipOutputStream(fout);

		// create File object from source directory
		// File fileSource = new File(sourcePath);

		addDirectory(zout, sourcePath);

		// close the ZipOutputStream
		zout.close();

	}

	/**
	 * Given a location of a zip file, this method will extract the content of
	 * the zip file to the target path.
	 * 
	 * @param sourcePath
	 *            of the zip file location.
	 * @param targetPath
	 *            to extract the content of the zip file.
	 * @throws IOException
	 *             if the path's are not correct.
	 */
	public void extractZip(File sourcePath, File targetPath) throws IOException {

		UncompressedFile u = null;

		u = extract(sourcePath.toString(), targetPath.toString());

		saveUncompressedFile(u);

	}

	private static void addDirectory(ZipOutputStream zout, File fileSource)
			throws IOException {

		// get sub-folder/files list
		File[] files = fileSource.listFiles();

		System.out.println("Adding directory " + fileSource.getName());

		for (int i = 0; i < files.length; i++) {
			// if the file is directory, call the function recursively
			if (files[i].isDirectory()) {
				addDirectory(zout, files[i]);
				continue;
			}

			/*
			 * we are here means, its file and not directory, so add it to the
			 * zip file
			 */

			System.out.println("Adding file " + files[i].getName());

			// create byte buffer
			byte[] buffer = new byte[1024];

			// create object of FileInputStream
			FileInputStream fin = new FileInputStream(files[i]);

			zout.putNextEntry(new ZipEntry(files[i].getName()));

			/*
			 * After creating entry in the zip file, actually write the file.
			 */
			int length;

			while ((length = fin.read(buffer)) > 0) {
				zout.write(buffer, 0, length);
			}

			/*
			 * After writing the file to ZipOutputStream, use
			 * 
			 * void closeEntry() method of ZipOutputStream class to close the
			 * current entry and position the stream to write the next entry.
			 */

			zout.closeEntry();

			// close the InputStream
			fin.close();

		}

	}

	private static UncompressedFile extract(String sourcePath, String targetPath)
			throws IOException {
		byte[] zip = fileToBytes(sourcePath);
		return extract(zip, targetPath);
	}

	private static UncompressedFile extract(byte[] zip, String targetPath)
			throws IOException {
		ZipEntry oEntry;
		ZipInputStream zIn;
		ByteArrayOutputStream byto;

		ByteArrayInputStream ba = new ByteArrayInputStream(zip);
		zIn = new ZipInputStream(ba);

		UncompressedFile u = new UncompressedFile(targetPath);

		while (true) {
			try {
				oEntry = zIn.getNextEntry();

				if (oEntry == null)
					break;

				byto = new ByteArrayOutputStream();
				int i;
				byte[] bytData = new byte[1000];
				int k = 0;
				while (true) {
					i = zIn.read(bytData);
					if (i == -1)
						break;
					byto.write(bytData, 0, i);
					k += i;
				}
				zIn.closeEntry();
				u.addFile(new BinaryFileData(oEntry.getName(), byto
						.toByteArray()));
				oEntry = null;
			} catch (Exception e) {
				e.printStackTrace();
			}
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return u;

	}

	private static byte[] fileToBytes(String path) throws IOException {
		File f = new File(path);
		FileInputStream fi = new FileInputStream(f);
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		byte[] bytData = new byte[1000];
		int i = 0;
		while (true) {
			i = fi.read(bytData);
			if (i == -1)
				break;
			bo.write(bytData, 0, i);
		}
		return bo.toByteArray();
	}

	private static void saveUncompressedFile(UncompressedFile uncompressedFile) {
		File f = new File(uncompressedFile.getPath());
		Log.d("getPath", uncompressedFile.getPath());
		if (!(f.exists() && f.isDirectory())) {
			f.mkdirs();
		}
		for (BinaryFileData b : uncompressedFile.getFiles().values()) {
			try {
				byteArrayToFile(
						uncompressedFile.getPath() + "/" + b.getFileName(),
						b.getData());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private static void byteArrayToFile(String path, byte[] data)
			throws IOException {
		FileOutputStream fo = new FileOutputStream(path);
		fo.write(data);
		fo.flush();
		fo.close();
	}

}
